/*
 * Copyright 2004-2014 SmartBear Software
 *
 * Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
 * versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 *
 * http://ec.europa.eu/idabc/eupl
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
 * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the Licence for the specific language governing permissions and limitations
 * under the Licence.
*/

package com.eviware.soapui.support.editor.inspectors.attachments;

import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
import com.eviware.soapui.impl.wsdl.AttachmentContainer;
import com.eviware.soapui.impl.wsdl.MutableAttachmentContainer;
import com.eviware.soapui.impl.wsdl.support.HelpUrls;
import com.eviware.soapui.impl.wsdl.support.PathUtils;
import com.eviware.soapui.impl.wsdl.support.WsdlAttachment;
import com.eviware.soapui.model.iface.Attachment;
import com.eviware.soapui.model.iface.MessagePart.AttachmentPart;
import com.eviware.soapui.support.StringUtils;
import com.eviware.soapui.support.Tools;
import com.eviware.soapui.support.UISupport;
import com.eviware.soapui.support.components.JXToolBar;
import com.eviware.soapui.support.swing.JTableFactory;

import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.Component;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.List;

/**
 * Utility Panel for displaying a table of attachments
 *
 * @author emibre
 */

public class AttachmentsPanel extends javax.swing.JPanel {
    private DropTarget dropTarget;
    private FileTransferHandler fileTransferHandler;
    private AttachmentsTableModel tableModel;
    private JFileChooser fc;
    private final AttachmentContainer container;
    private JButton exportBtn;
    private JButton reloadBtn;

    /**
     * Creates new form FileTableList
     */
    public AttachmentsPanel(AttachmentContainer container) {
        this.container = container;
        initComponents();
        initFileTransfer();
    }

    public void release() {
        tableModel.release();
        if (attachmentPartCellEditor != null) {
            attachmentPartCellEditor.release();
        }
    }

    private void initFileTransfer() {
        if (container instanceof MutableAttachmentContainer) {
            fileTransferHandler = new FileTransferHandler(tableModel);
            fileTable.setDragEnabled(true);
            fileTable.setTransferHandler(fileTransferHandler);

            dropTarget = new DropTarget();
            dropTarget.setActive(true);
            try {
                dropTarget.addDropTargetListener(new DropTargetListener() {
                    public void dragEnter(DropTargetDragEvent dtde) {
                    }

                    public void dragExit(DropTargetEvent dte) {
                    }

                    public void dragOver(DropTargetDragEvent dtde) {
                    }

                    @SuppressWarnings("unchecked")
                    public void drop(DropTargetDropEvent dtde) {
                        try {
                            dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
                            Transferable trans = dtde.getTransferable();
                            List<File> files = (List<File>) trans.getTransferData(DataFlavor.javaFileListFlavor);
                            for (File f : files) {
                                System.out.println("Dropping file: " + f.getName());

                                Boolean retval = UISupport.confirmOrCancel("Cache attachment in request?", "Att Attachment");
                                if (retval == null) {
                                    return;
                                }

                                tableModel.addFile(f, retval);
                            }

                        } catch (Exception e) {
                            SoapUI.logError(e);
                        }
                    }

                    public void dropActionChanged(DropTargetDragEvent dtde) {
                    }
                });
            } catch (Exception e) {
                SoapUI.logError(e);
            }

            jScrollPane1.getViewport().setDropTarget(dropTarget);
        }
    }

    private void initComponents() {
        jScrollPane1 = new javax.swing.JScrollPane();
        tableModel = new AttachmentsTableModel(container);
        fileTable = JTableFactory.getInstance().makeJTable(tableModel);

        if (container instanceof MutableAttachmentContainer) {
            attachmentPartCellEditor = new AttachmentPartCellEditor();
            fileTable.getColumnModel().getColumn(3).setCellEditor(attachmentPartCellEditor);
        }

        setLayout(new java.awt.BorderLayout());
        jScrollPane1.setViewportView(fileTable);

        add(jScrollPane1, java.awt.BorderLayout.CENTER);

        jPanel1 = UISupport.createSmallToolbar();

        if (container instanceof MutableAttachmentContainer) {
            addFileBtn = UISupport.createToolbarButton(UISupport.createImageIcon("/add_property.gif"));
            removeBtn = UISupport.createToolbarButton(UISupport.createImageIcon("/remove_property.gif"));
            reloadBtn = UISupport.createToolbarButton(UISupport.createImageIcon("/reload_properties.gif"));

            addFileBtn.setToolTipText("Adds an attachment");
            addFileBtn.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    addFileBtnActionPerformed(evt);
                }
            });

            jPanel1.addFixed(addFileBtn);

            removeBtn.setToolTipText("Removes the selected attachment");
            removeBtn.setEnabled(false);
            removeBtn.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    removeBtnActionPerformed(evt);
                }
            });

            jPanel1.addFixed(removeBtn);

            reloadBtn.setToolTipText("Reloads the selected attachment");
            reloadBtn.setEnabled(false);
            reloadBtn.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    reloadBtnActionPerformed(evt);
                }
            });

            jPanel1.addFixed(reloadBtn);
        }

        exportBtn = UISupport.createToolbarButton(UISupport.createImageIcon("/export.gif"));
        exportBtn.setToolTipText("Exports the selected attachment to a file");
        exportBtn.setEnabled(false);
        exportBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                exportBtnActionPerformed(evt);
            }
        });

        jPanel1.addFixed(exportBtn);
        jPanel1.addGlue();
        jPanel1.addFixed(UISupport.createToolbarButton(new ShowOnlineHelpAction(HelpUrls.ATTACHMENTS_HELP_URL)));
        add(jPanel1, java.awt.BorderLayout.NORTH);

        fileTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                if (removeBtn != null) {
                    removeBtn.setEnabled(fileTable.getSelectedRowCount() > 0);
                    reloadBtn.setEnabled(fileTable.getSelectedRowCount() > 0);
                }

                exportBtn.setEnabled(fileTable.getSelectedRowCount() > 0);
            }
        });

        fileTable.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() < 2) {
                    return;
                }

                int ix = fileTable.getSelectedRow();
                if (ix == -1) {
                    return;
                }

                Attachment attachment = container.getAttachmentAt(ix);

                if (attachment.isCached()) {
                    String name = attachment.getName();
                    try {
                        name = StringUtils.createFileName(name, '-');
                        File tempFile = File.createTempFile("attachment-" + name,
                                "." + ContentTypeHandler.getExtensionForContentType(attachment.getContentType()));
                        exportAttachment(tempFile, attachment, false);
                    } catch (Exception e1) {
                        UISupport.showErrorMessage(e1);
                    }
                } else {
                    Tools.openURL(attachment.getUrl());
                }
            }
        });
    }

    protected void exportBtnActionPerformed(ActionEvent evt) {
        File file = UISupport.getFileDialogs().saveAs(this, "Export Attachment..");
        while (file != null && file.exists()
                && !UISupport.confirm("File " + file.getName() + " exists, overwrite?", "Export Attachment")) {
            file = UISupport.getFileDialogs().saveAs(this, "Export Attachment..");
        }

        if (file != null) {
            Attachment attachment = tableModel.getAttachmentAt(fileTable.getSelectedRow());
            try {
                exportAttachment(file, attachment, true);
            } catch (Exception e) {
                UISupport.showErrorMessage(e);
            }
        }
    }

    private void exportAttachment(File file, Attachment attachment, boolean showOpenQuery)
            throws FileNotFoundException, IOException, Exception, MalformedURLException {
        FileOutputStream out = new FileOutputStream(file);

        long total = Tools.writeAll(out, attachment.getInputStream());
        out.close();
        if (!showOpenQuery
                || UISupport.confirm("Written [" + total + "] bytes to " + file.getName() + ", open in browser?",
                "Saved File")) {
            Tools.openURL(file.toURI().toURL().toString());
        }
    }

    protected void reloadBtnActionPerformed(ActionEvent evt) {
        int selectedRow = fileTable.getSelectedRow();
        if (selectedRow == -1) {
            return;
        }

        WsdlAttachment attachment = (WsdlAttachment) tableModel.getAttachmentAt(selectedRow);
        if (attachment == null) {
            return;
        }

        File file = UISupport.getFileDialogs().open(this, "Reload Attachment..", "*", "Any File", attachment.getUrl());
        if (file != null) {
            Boolean retval = UISupport.confirmOrCancel("Cache attachment in request?", "Reload Attachment");
            if (retval == null) {
                return;
            }

            try {
                attachment.reload(file, retval);
                tableModel.fireTableRowsUpdated(selectedRow, selectedRow);
            } catch (IOException e) {
                UISupport.showErrorMessage(e);
            }
        }
    }

    private void addFileBtnActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_addFileBtnActionPerformed
        if (fc == null) {
            fc = new JFileChooser();
        }

        String root = PathUtils.getExpandedResourceRoot(container.getModelItem());
        if (StringUtils.hasContent(root)) {
            fc.setCurrentDirectory(new File(root));
        }

        int returnVal = fc.showOpenDialog(this);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();
            Boolean retval = UISupport.confirmOrCancel("Cache attachment in request?", "Add Attachment");
            if (retval == null) {
                return;
            }
            try {
                tableModel.addFile(file, retval);
            } catch (IOException e) {
                UISupport.showErrorMessage(e);
            }
        } else {
            System.out.println("Open command cancelled by user.");
        }
    }// GEN-LAST:event_addFileBtnActionPerformed

    private void removeBtnActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_removeBtnActionPerformed
        if (UISupport.confirm("Remove selected attachments?", "Remove Attachments")) {
            tableModel.removeAttachment(fileTable.getSelectedRows());
        }
    }// GEN-LAST:event_removeBtnActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton addFileBtn;
    private JTable fileTable;
    private JXToolBar jPanel1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JButton removeBtn;
    private AttachmentPartCellEditor attachmentPartCellEditor;

    // End of variables declaration//GEN-END:variables

    private class AttachmentPartCellEditor extends DefaultCellEditor {
        public AttachmentPartCellEditor() {
            super(new JComboBox(new PartsComboBoxModel()));
        }

        public void release() {
            ((PartsComboBoxModel) ((JComboBox) editorComponent).getModel()).release();
        }

        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
            PartsComboBoxModel model = ((PartsComboBoxModel) ((JComboBox) editorComponent).getModel());
            ((JComboBox) editorComponent).setModel(model.init(tableModel.getAttachmentAt(row)));

            return super.getTableCellEditorComponent(table, value, isSelected, row, column);
        }
    }

    private final class PartsComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener {
        private Attachment attachment;
        private AttachmentPart[] parts;

        public PartsComboBoxModel() {
            container.addAttachmentsChangeListener(this);
        }

        public void release() {
            container.removeAttachmentsChangeListener(this);
        }

        public PartsComboBoxModel init(Attachment attachment) {
            this.attachment = attachment;

            int previousPartsCount = parts == null ? 0 : parts.length;

            parts = container.getDefinedAttachmentParts();
            if (previousPartsCount < parts.length) {
                fireIntervalAdded(this, previousPartsCount, parts.length);
            } else if (previousPartsCount > parts.length) {
                fireIntervalRemoved(this, parts.length - 1, previousPartsCount);
            }

            fireContentsChanged(this, 0, parts.length - 1);

            return this;
        }

        public Object getElementAt(int index) {
            return parts == null ? null : parts[index].getName();
        }

        public int getSize() {
            return parts == null ? 0 : parts.length;
        }

        public Object getSelectedItem() {
            return attachment == null ? null : attachment.getPart();
        }

        public void setSelectedItem(Object anItem) {
            if (attachment != null) {
                attachment.setPart((String) anItem);
            }
        }

        public void propertyChange(PropertyChangeEvent arg0) {
            // delete our current one?
            if (arg0.getOldValue() == attachment && arg0.getNewValue() == null) {
                attachment = null;
                parts = null;
            }
        }
    }
}
